# -*- coding: utf-8 -*-
"""
-----------------------------------------------------------------
    File Name   : handle.py
    Description : 核心处理程序
    Author      : DuanGe
    Created On  : 2024/11/19
    Email       : duangeali@aliyun.com
    Version     : 1.0.0
    Copyright   : Copyright (c) 2024 DuanGe
    License     : Apache License 2.0
-----------------------------------------------------------------
"""
import datetime
import json
import os
import re
import threading
import time
import uuid

import schedule

import utils
from common import constant, common
from common.log import action_logger, add_logs


def get_school():
    """获取学校列表"""
    # 是否存在配置文件
    if os.path.exists(constant.SCHOOL_FILE_PATH):
        data = utils.file_util.read_json(constant.SCHOOL_FILE_PATH)
        msg = f"路径: {constant.SCHOOL_FILE_PATH}"
    else:
        data = json.loads(utils.request_util.send_get_request(constant.SCHOOL_URL).content).get('unitsList', None)
        msg = f"路径: {constant.SCHOOL_URL}"
        # 写入配置文件
        utils.file_util.write_json(constant.SCHOOL_FILE_PATH, data)
    if data is None:
        common.error_tip("学校列表为空")
        common.info_tip("请配置用户信息")
        raise ValueError("school list is empty")
    # 记录日志
    add_logs(action_logger, "INFO", "获取学校列表", msg)
    return data


def show_school_table(school_list: list[dict]):
    """
    显示学校

    :param school_list: 学校数据
    """
    utils.table_util.list_table(
        list_data=school_list,
        table_headers=['学校UID', '学校名称'],
        columns=['uid', 'name']
    )


def select_school_callback(val: str, school_list: list[dict], user: dict, operation: str):
    """
    选择学校

    :param val: 输入的学校UID
    :param school_list: 学校列表
    :param user: 用户配置对象
    :param operation: 操作名称
    """
    if not val.isdigit():
        common.info_tip("UID不是数字, 请重新输入")
        return False
    uid = int(val)
    # 根据UID找对应学校
    for school in school_list:
        if school['uid'] == uid:
            print("[你选择的学校信息如下]")
            show_school_table([school])
            # 将学校信息保存到用户配置对象
            user['school'] = school
            # 记录日志
            add_logs(action_logger, "INFO", operation + "-选择学校", f"选择的学校: {school['name']}")
            return True
    common.info_tip("UID不存在, 请重新输入")
    return False


def get_server_url(user: dict):
    """
    获取学校服务器地址

    :param user: 用户配置对象
    :return: 服务器地址
    """
    server_url = user.get('school', {}).get('serverUrl', None)
    if server_url is None:
        common.error_tip("学校服务器地址为空")
        common.info_tip("请配置用户信息")
        raise ValueError("serverUrl is empty")
    # 记录日志
    add_logs(action_logger, "INFO", "获取学校服务器地址", f"地址: {server_url}")
    return server_url


def user_login(user: dict, operation: str):
    """
    用户登录

    :param user: 用户配置对象
    :param operation: 操作名称
    """
    url = get_server_url(user) + constant.USER_URL
    while True:
        username = input("请输入账户: ")
        password = input("请输入密码: ")
        response = utils.request_util.send_post_request(url, json={"username": username, "password": password})
        data = json.loads(response.content)
        msg = f"响应消息: {data.get('returnMsg', None)}, 返回值: {data.get('returnValue', None)}"
        # 登录失败
        if not data.get('returnValue', None):
            common.error_tip(data.get('returnMsg', '登录失败'))
            # 记录日志
            add_logs(action_logger, "ERROR", operation + "-用户登录", msg, exc_info=False)
            flag = input("是否重新登录?(y/n)")
            if flag == 'n' or flag == 'N':
                return False
            continue
        # 保存参数信息
        data['dervicetype'] = "ANDROID"
        data['version'] = "12"
        data['token'] = uuid.uuid4().hex
        # 保存用户信息
        user['userInfo'] = data
        if data.get('returnMsg', None):
            common.info_tip(data['returnMsg'])
        # 记录日志
        add_logs(action_logger, "INFO", operation + "-用户登录", msg)
        return True


def bind_user(user: dict, operation: str):
    """
    绑定用户

    :param user: 用户配置对象
    :param operation: 操作名称
    """
    operation += "-绑定用户"
    url = get_server_url(user) + constant.UP_TOKEN_URL
    # 绑定用户，获取Cookie信息
    response = utils.request_util.send_post_request(
        url,
        params={
            "dervicetype": user['userInfo']['dervicetype'],
            "usercode": user['userInfo']['cardNo'],
            "version": user['userInfo']['version'],
            "token": user['userInfo']['token'],
        }
    )
    data = json.loads(response.content)
    msg = f"响应消息: {data.get('msg', None)}, 返回值: {data.get('returnvalue', None)}"
    # 绑定失败
    if data.get('msg', None) != "绑定成功":
        common.error_tip(data.get('msg', '绑定失败'))
        # 记录日志
        add_logs(action_logger, "ERROR", operation + "-绑定用户", msg, exc_info=False)
        return False
    # 获取cookie信息并保存
    cookie = dict(response.cookies).get('JSESSIONID')
    user['userInfo']['cookie'] = "JSESSIONID=" + cookie
    common.info_tip(data['msg'])
    # 记录日志
    add_logs(action_logger, "INFO", operation + "-绑定用户", msg)
    # 将用户信息写入配置文件
    utils.file_util.write_json(constant.USER_FILE_PATH, user)
    return True


def get_room(user: dict = None, operation: str = None):
    """
    获取教室

    :param user: 用户配置对象
    :param operation: 操作名称
    """
    # 用户配置对象不存在则读取配置文件
    if not user:
        room_list = utils.file_util.read_json(constant.ROOM_FILE_PATH)
        if room_list is None:
            common.error_tip("教室列表为空")
            common.info_tip("请配置用户信息")
            raise ValueError("The room_list is empty")
        # 记录日志
        add_logs(action_logger, "INFO", "获取教室", f"路径: {constant.ROOM_FILE_PATH}")
        return room_list
    url = get_server_url(user) + constant.ROOM_URL
    params = {"username": user.get('userInfo', {}).get('cardNo'), "password": user.get('userInfo', {}).get('passWord')}
    headers = {"Cookie": user.get('userInfo', {}).get('cookie')}
    response = utils.request_util.send_get_request(url, params, headers)
    data = json.loads(response.content)
    msg = f"响应消息: {data.get('Msg', None)}, 返回值: {data.get('ReturnValue', None)}"
    # 获取失败
    if data.get('Msg', None) != "获取成功":
        common.error_tip(data.get('Msg', '获取失败'))
        # 记录日志
        add_logs(action_logger, "ERROR", operation + "-获取教室", msg, exc_info=False)
        return False
    room_list = data.get('Data')
    common.info_tip(data['Msg'])
    # 记录日志
    add_logs(action_logger, "INFO", operation + "-获取教室", msg)
    # 将教室信息写入配置文件
    utils.file_util.write_json(constant.ROOM_FILE_PATH, room_list)
    return True


def set_user():
    """配置用户信息"""
    operation = "配置用户信息"
    print("开始配置用户信息...")
    # 初始化用户配置对象
    user = {}
    # 获取学校列表
    school_list = get_school()
    if school_list is None:
        return common.error_tip(f"{operation}失败")
    # 选择学校
    common.input_loop(
        prompt="请输入你学校的UID: ",
        callback=select_school_callback,
        before=show_school_table,
        repeat=False,
        school_list=school_list,
        user=user,
        operation=operation
    )
    # 初始化标志
    bind_result = False
    room_result = False
    # 登录
    common.info_tip("请先登录账号")
    login_result = user_login(user, operation)
    if login_result:
        # 绑定用户，获取Cookie信息
        bind_result = bind_user(user, operation)
        if bind_result:
            # 获取教室列表信息
            room_result = get_room(user, operation)
    if login_result and bind_result and room_result:
        # 记录日志
        add_logs(action_logger, "INFO", operation)
        return common.info_tip(f"{operation}成功")
    common.error_tip(f"{operation}失败")
    # 记录日志
    add_logs(action_logger, "ERROR", operation, exc_info=False)


def get_user():
    """获取用户配置信息"""
    user_info = utils.file_util.read_json(constant.USER_FILE_PATH)
    if user_info is None:
        common.error_tip("用户配置信息为空")
        common.info_tip("请先配置用户信息")
        raise ValueError("The user_info is empty")
    # 记录日志
    add_logs(action_logger, "INFO", "获取用户配置信息", f"路径: {constant.USER_FILE_PATH}")
    return user_info


# 时间段
timeSlotSet = frozenset(["forenoon", "afternoon"])


def time_slot_exists(time_slot: str):
    """
    时间段是否存在

    :param time_slot: 时间段
    :return: 有效的时间段(小写)
    """
    time_slot_lower = str(time_slot).lower()
    # 检查日志级别是否有效
    if time_slot_lower not in timeSlotSet:
        raise ValueError("无效的时间段")
    return time_slot_lower


def get_room_seat(time_slot: str):
    """
    获取指定时间段的教室和座位信息

    :param time_slot: 时间段(forenoon/afternoon)
    """
    time_slot = time_slot_exists(time_slot)
    # 教室和座位信息
    room_seat = []
    # 读取教室和座位信息配置文件
    file_data = utils.file_util.read_json(constant.ROOM_SEAT_FILE_PATH)
    # 获取当前时间段
    current_time_slot = "forenoon" if common.is_morning() else "afternoon"
    if time_slot != current_time_slot:
        if file_data:
            room_seat = file_data.get(time_slot, [])
        if room_seat:
            # 记录日志
            add_logs(action_logger, "INFO", f"获取{time_slot}教室和座位信息", f"路径: {constant.ROOM_SEAT_FILE_PATH}")
            return room_seat
        return common.info_tip(f"现在是{'上午' if current_time_slot == 'forenoon' else '下午'}"
                               f", 请在{'上午' if time_slot == 'forenoon' else '下午'}进行配置")
    print("正在获取教室座位列表, 请稍等...")
    user = get_user()
    room = get_room()
    # 提取通用信息
    server_url = get_server_url(user)
    headers = {"Cookie": user['userInfo']['cookie']}
    # 获取教室列表和座位列表信息
    for item in room:
        roomno = item['ControlNo']
        # 获取认证
        response = utils.request_util.send_get_request(
            url=server_url + constant.AUTHENTICATION_URL,
            params={"roomno": roomno},
            headers=headers
        )
        data = json.loads(response.content)
        msg = f"响应消息: {data.get('Msg', None)}, 返回值: {data.get('ReturnValue', None)}"
        # 认证通过
        if data.get('ReturnValue', None) == 0:
            can_bask_id = data['CanBaskID']
            # 获取教室信息
            response = utils.request_util.send_get_request(
                url=server_url + constant.CURRENT_ROOM_URL,
                params={"beskids": can_bask_id, "roomno": roomno},
                headers=headers
            )
            data = json.loads(response.content)
            msg = f"响应消息: {data.get('Msg', None)}, 返回值: {data.get('ReturnValue', None)}"
            # 获取教室信息成功
            if data.get('ReturnValue', None) == 0:
                room_info = data.get('Data')[0]
                room_info['CanBaskID'] = can_bask_id
                # 获取座位信息
                response = utils.request_util.send_get_request(
                    url=server_url + constant.CURRENT_ROOM_SEAT_URL,
                    params={"canusetimeid": room_info['id'], "roomno": roomno},
                    headers=headers
                )
                data = json.loads(response.content)
                msg = f"响应消息: {data.get('Msg', None)}, 返回值: {data.get('ReturnValue', None)}"
                # 获取座位信息成功
                if data.get('ReturnValue', None) == 0:
                    seat = data.get('Data')
                    room_info['seat'] = seat
                    room_seat.append(room_info)
                elif data.get('ReturnValue', None) == -1:
                    add_logs(action_logger, "ERROR", "获取座位信息", msg, exc_info=False)
            elif data.get('ReturnValue', None) == -1:
                add_logs(action_logger, "ERROR", "获取教室信息", msg, exc_info=False)
        elif data.get('ReturnValue', None) == -1:
            add_logs(action_logger, "ERROR", "获取认证", msg, exc_info=False)
    if len(room_seat) <= 0:
        if file_data:
            room_seat = file_data.get(time_slot, [])
        if room_seat:
            # 记录日志
            add_logs(action_logger, "INFO", f"获取{time_slot}教室和座位信息", f"路径: {constant.ROOM_SEAT_FILE_PATH}")
            return room_seat
        return common.info_tip("没有可以预约的教室")
    # 避免file_data为None的情况
    file_data = {} if file_data is None else file_data
    file_data[time_slot] = room_seat
    # 写入配置文件
    utils.file_util.write_json(constant.ROOM_SEAT_FILE_PATH, file_data)
    # 记录日志
    add_logs(action_logger, "INFO", f"获取{time_slot}教室和座位信息", "发送请求")
    return room_seat


def show_room_table(room_seat: list[dict]):
    """
    显示教室信息

    :param room_seat: 教室数据
    """
    utils.table_util.list_table(
        list_data=room_seat,
        table_headers=['教室ID', '教室名称', '开始时间', '结束时间'],
        columns=['id', 'roomName', 'beginTime', 'endTime']
    )


def show_seat_table(seat: list[dict]):
    """
    显示座位信息

    :param seat: 座位数据
    """
    utils.table_util.list_table(
        list_data=seat,
        table_headers=['座位ID', '座位名称', '座位状态'],
        columns=['TableID', 'TableNo', 'CanBesk']
    )


def set_seat_callback(val: str, room: dict, seat: list[dict], fixed_seat: list[dict], card_no: str):
    """
    添加固定抢座座位信息

    :param val: 输入的座位名称
    :param room: 座位所属教室信息
    :param seat: 座位列表
    :param fixed_seat: 固定抢座信息
    :param card_no: 用户账号
    """
    # 座位是否已添加
    if any(item['tableno'] == val for item in fixed_seat):
        common.info_tip("该座位已添加, 请输入其他座位")
        return False
    for item in seat:
        if item['TableNo'] == val:
            fixed_seat.append({
                "roomName": room['roomName'],
                "begintime": room['beginTime'],
                "beskid": room['id'],
                "endtime": room['endTime'],
                "readerno": card_no,
                "roomno": room['roomNo'],
                "tableid": item['TableID'],
                "tableno": item['TableNo']
            })
            return True
    common.info_tip("座位名称不存在, 请重新输入")
    return False


def set_fixed_seat_callback(val: str, room_seat: list[dict], fixed_seat: list[dict], card_no: str):
    """
    配置固定抢座信息

    :param val: 输入的教室ID
    :param room_seat: 教室和座位信息
    :param fixed_seat: 固定抢座信息
    :param card_no: 用户账号
    """
    if not val.isdigit():
        common.info_tip("教室ID不是数字, 请重新输入")
        return False
    room_id = int(val)
    # 根据room_id找对应的教室
    for room in room_seat:
        if room['id'] == room_id:
            # 座位信息
            seat = room['seat']
            show_seat_table(seat)
            while True:
                common.input_loop(
                    prompt="请输入要选择的座位[座位名称]: ",
                    callback=set_seat_callback,
                    room=room,
                    seat=seat,
                    fixed_seat=fixed_seat,
                    card_no=card_no
                )
                flag = input("是否继续选择座位?(y/n)")
                if flag == 'n' or flag == 'N':
                    return True
    common.info_tip("教室ID不存在, 请重新输入")
    return False


def set_auto_seat_callback(val: str, room_seat: list[dict], auto_room: list[dict], auto_seat: list[dict], card_no: str):
    """
    配置自动抢座信息

   :param val: 输入的教室ID
   :param room_seat: 教室和座位信息
   :param auto_room: 自动抢座教室信息
   :param auto_seat: 自动抢座座位信息
   :param card_no: 用户账号
   """
    if not val.isdigit():
        common.info_tip("教室ID不是数字, 请重新输入")
        return False
    room_id = int(val)
    # 教室是否已添加
    if any(item['id'] == room_id for item in auto_room):
        common.info_tip("该教室已添加, 请输入其他教室")
        return False
    # 根据room_id找对应的教室
    for room in room_seat:
        if room['id'] == room_id:
            # 座位信息
            seat = room['seat']
            for item in seat:
                auto_seat.append({
                    "roomName": room['roomName'],
                    "begintime": room['beginTime'],
                    "beskid": room['id'],
                    "endtime": room['endTime'],
                    "readerno": card_no,
                    "roomno": room['roomNo'],
                    "tableid": item['TableID'],
                    "tableno": item['TableNo']
                })
            auto_room.append({
                "id": room['id'],
                "roomName": room['roomName'],
                "beginTime": room['beginTime'],
                "endTime": room['endTime'],
            })
            return True
    common.info_tip("教室ID不存在, 请重新输入")
    return False


def set_start_time_callback(val: str, start_time: dict, time_slot: str):
    """
    配置抢座时间

    :param val: 输入的时间
    :param start_time: 抢座时间信息
    :param time_slot: 时间段(forenoon/afternoon)
    """
    time_slot = time_slot_exists(time_slot)
    # 定义时间格式的正则表达式
    time_pattern = r'^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)$'
    # 使用正则表达式匹配输入的时间字符串
    match = re.match(time_pattern, val)
    if match:
        # 提取小时部分
        hour = int(match.group(1))
        if time_slot == "forenoon":
            if 0 <= hour < 12:
                start_time['startTime'] = val
                return True
            else:
                common.info_tip("时间不在00:00:00-11:59:59范围内, 请重新输入")
                return False
        elif time_slot == "afternoon":
            if 12 <= hour < 24:
                start_time['startTime'] = val
                return True
            else:
                common.info_tip("时间不在12:00:00-23:59:59范围内, 请重新输入")
                return False
        else:
            common.info_tip("输入时间段不正确, 请重新输入")
            return False
    else:
        common.info_tip("输入格式不正确, 请重新输入")
        return False


def get_grab_seat():
    """获取抢座信息"""
    data = {}
    # 抢座信息配置文件不存
    if not os.path.exists(constant.GRAB_SEAT_FILE_PATH):
        utils.file_util.write_json(constant.GRAB_SEAT_FILE_PATH, data)
    else:
        file_data = utils.file_util.read_json(constant.GRAB_SEAT_FILE_PATH)
        # 抢座信息不为None
        if file_data is not None:
            data = file_data
    # 记录日志
    add_logs(action_logger, "INFO", "获取抢座信息", f"路径: {constant.GRAB_SEAT_FILE_PATH}")
    return data


def show_seat_room_table(seat: list[dict]):
    """
    显示座位信息(带教室)

    :param seat: 座位数据
    """
    utils.table_util.list_table(
        list_data=seat,
        table_headers=['教室名称', '座位名称', '开始时间', '结束时间'],
        columns=['roomName', 'tableno', 'begintime', 'endtime']
    )


def show_start_time_table(start_time: str):
    """
    显示开始抢座的时间

    :param start_time: 抢座时间
    """
    utils.table_util.list_table(
        list_data=[{'time': start_time}],
        table_headers=['开始抢座的时间'],
        columns=['time']
    )


def show_clock_in_time_table(clock_in_time: str):
    """
    显示开始签到的时间

    :param clock_in_time: 抢座时间
    """
    utils.table_util.list_table(
        list_data=[{'time': clock_in_time}],
        table_headers=['开始签到的时间'],
        columns=['time']
    )


def print_grab_seat(grab_seat: dict, time_slot: str):
    """
    打印抢座配置信息

    :param grab_seat: 抢座信息
    :param time_slot: 时间段(forenoon/afternoon)
    """
    time_slot = time_slot_exists(time_slot)
    tip = "上午" if time_slot == "forenoon" else "下午"
    current_grab_seat = grab_seat.get(time_slot, {})
    if not current_grab_seat:
        # 记录日志
        add_logs(action_logger, "ERROR", f"打印{tip}抢座配置信息", f"时间段: {tip}", f"暂无{tip}抢座配置信息",
                 exc_info=False)
        return common.info_tip(f"暂无{tip}抢座配置信息")
    print(f"[{tip}固定要抢的座位信息]")
    show_seat_room_table(current_grab_seat.get('fixedSeat', []))
    print(f"[{tip}自动抢座的教室信息]")
    show_room_table(current_grab_seat.get('autoRoom', []))
    print(f"[{tip}开始抢座时间的信息]")
    show_start_time_table(current_grab_seat.get('startTime', ''))
    print(f"[{tip}开始签到时间的信息]")
    show_clock_in_time_table(current_grab_seat.get('clockInTime', ''))
    # 记录日志
    add_logs(action_logger, "INFO", f"打印{tip}抢座配置信息", f"时间段: {tip}")


def encrypt_seats_property():
    """加密座位属性"""

    def _encryption(seat_list: list[dict]):
        """加密座位的辅助函数"""
        for seat in seat_list:
            seat['checkstr'] = uuid.uuid4().hex

    grab_seat = get_grab_seat()
    # 遍历上午和下午的抢座配置信息
    for time_slot in timeSlotSet:
        slot_data = grab_seat.get(time_slot, {})
        for seat_type in ['fixedSeat', 'autoSeat']:
            seats = slot_data.get(seat_type, [])
            if seats:
                _encryption(seats)
                slot_data[seat_type] = seats
    utils.file_util.write_json(constant.GRAB_SEAT_FILE_PATH, grab_seat)
    # 记录日志
    add_logs(action_logger, "INFO", "加密座位属性", f"路径: {constant.GRAB_SEAT_FILE_PATH}")


def set_grab_seat(operation: str, time_slot: str, room_seat: list[dict]):
    """
    配置抢座信息

    :param operation: 操作名称
    :param time_slot: 时间段(forenoon/afternoon)
    :param room_seat: 教室和座位信息
    """
    if not (room_seat and time_slot):
        return common.error_tip(f"配置{operation}抢座信息失败")
    time_slot = time_slot_exists(time_slot)
    # 获取用户信息
    user = get_user()
    card_no = user.get('userInfo', {}).get('cardNo', None)
    print(f"开始配置{operation}抢座信息...")
    print(f"[配置{operation}抢座固定要抢座位的信息]")
    # 初始化固定抢座信息
    fixed_seat = []
    while True:
        common.input_loop(
            prompt="请输入要选择的教室[教室ID]: ",
            callback=set_fixed_seat_callback,
            before=show_room_table,
            repeat=False,
            room_seat=room_seat,
            fixed_seat=fixed_seat,
            card_no=card_no
        )
        flag = input("是否继续选择教室?(y/n)")
        if flag == 'n' or flag == 'N':
            break
    print(f"[配置{operation}抢座自动要抢座位的教室信息]")
    # 初始化自动抢座信息
    auto_room = []
    auto_seat = []
    while True:
        common.input_loop(
            prompt="请输入要选择的教室[教室ID]: ",
            callback=set_auto_seat_callback,
            before=show_room_table,
            repeat=False,
            room_seat=room_seat,
            auto_room=auto_room,
            auto_seat=auto_seat,
            card_no=card_no
        )
        flag = input("是否继续选择教室?(y/n)")
        if flag == 'n' or flag == 'N':
            break
    print(f"[配置{operation}抢座开始抢座时间的信息]")
    # 初始化抢座时间
    start_time = {"startTime": ""}
    common.input_loop(
        prompt=f"请输入{operation}抢座的时间[格式HH:mm:ss]: ",
        callback=set_start_time_callback,
        start_time=start_time,
        time_slot=time_slot,
    )
    print(f"[配置{operation}开始签到时间的信息]")
    # 初始化签到时间
    clock_in_time = {"startTime": ""}
    common.input_loop(
        prompt=f"请输入{operation}签到的时间[格式HH:mm:ss]: ",
        callback=set_start_time_callback,
        start_time=clock_in_time,
        time_slot=time_slot,
    )
    # 获取抢座信息
    grab_seat = get_grab_seat()
    # 组装抢座配置信息
    grab_seat[time_slot] = {
        "startTime": start_time['startTime'],
        "clockInTime": clock_in_time['startTime'],
        "fixedSeat": fixed_seat,
        "autoRoom": auto_room,
        "autoSeat": auto_seat
    }
    print_grab_seat(grab_seat, time_slot)
    # 是否保存配置
    while True:
        flag = input("是否保存配置?(y/n)")
        if flag == 'n' or flag == 'N':
            # 记录日志
            add_logs(action_logger, "ERROR", f"配置{operation}抢座信息", exc_info=False)
            return common.error_tip(f"配置{operation}抢座信息失败")
        # 将配置写入文件
        utils.file_util.write_json(constant.GRAB_SEAT_FILE_PATH, grab_seat)
        encrypt_seats_property()
        # 记录日志
        add_logs(action_logger, "INFO", f"配置{operation}抢座信息")
        return common.info_tip(f"配置{operation}抢座信息成功")


def set_forenoon_grab_seat():
    """配置上午抢座信息"""
    operation = "上午"
    # 获取上午时间段的教室和座位信息
    room_seat = get_room_seat("forenoon")
    set_grab_seat(operation, "forenoon", room_seat)


def set_afternoon_grab_seat():
    """配置下午抢座信息"""
    operation = "下午"
    # 获取下午时间段的教室和座位信息
    room_seat = get_room_seat("afternoon")
    set_grab_seat(operation, "afternoon", room_seat)


def show_user():
    """查看用户信息"""
    # 获取用户信息
    user = get_user()
    user_info = user.get('userInfo', {})
    user_info['name'] = user.get('school', {}).get('name', '')
    # 创建映射字典, 与原始JSON数据中的键进行对应
    field_mapping = {
        "学校": "name",
        "姓名": "readerName",
        "账号": "cardNo",
        "密码": "passWord",
        "类型": "readerType",
        "专业": "readerUnit",
    }
    # 生成新的结构
    result = [
        {"key": key, "val": user_info.get(value, "")}
        for key, value in field_mapping.items()
    ]
    utils.table_util.list_table(
        list_data=result,
        table_headers=['基础信息', '用户信息'],
        columns=['key', 'val']
    )


def show_forenoon_grab_seat():
    """查看上午抢座信息"""
    grab_seat = get_grab_seat()
    print_grab_seat(grab_seat, 'forenoon')


def show_afternoon_grab_seat():
    """查看下午抢座信息"""
    grab_seat = get_grab_seat()
    print_grab_seat(grab_seat, 'afternoon')


def get_reserve_history():
    """获取用户预约历史记录"""
    data = []
    # 预约历史记录配置文件不存
    if not os.path.exists(constant.RESERVE_HISTORY_FILE_PATH):
        utils.file_util.write_json(constant.RESERVE_HISTORY_FILE_PATH, data)
    else:
        file_data = utils.file_util.read_json(constant.RESERVE_HISTORY_FILE_PATH)
        # 预约历史记录不为None
        if file_data is not None:
            data = file_data
    # 记录日志
    add_logs(action_logger, "INFO", "获取用户预约历史记录信息", f"路径: {constant.RESERVE_HISTORY_FILE_PATH}")
    return data


def get_reserve_record():
    """获取当前预约记录"""
    user = get_user()
    # 提取通用信息
    url = get_server_url(user) + constant.USER_BESK_URL
    params = {"readerno": user.get('userInfo', {}).get('cardNo')}
    headers = {"Cookie": user.get('userInfo', {}).get('cookie')}
    # 获取当前预约记录
    while True:
        response = utils.request_util.send_get_request(url, params, headers, timeout=30)
        data = json.loads(response.content)
        msg = f"响应消息: {data.get('Msg', None)}, 返回值: {data.get('ReturnValue', None)}"
        if data.get('ReturnValue', None) == 0:
            # 记录日志
            add_logs(action_logger, "INFO", "获取当前预约记录", msg)
            return data.get('Data', [])
        else:
            # 记录日志
            add_logs(action_logger, "ERROR", "获取当前预约记录", msg, exc_info=False)
            return []


def show_reserve_record_table(reserve_record: list[dict]):
    """
    显示预约记录
    :param reserve_record: 预约记录列表
    """
    utils.table_util.list_table(
        list_data=reserve_record,
        table_headers=['教室名称', '座位名称', '使用日期', '开始时间', '结束时间', '提交时间'],
        columns=['roomName', 'tableNo', 'useDay', 'beginTime', 'endTime', 'commitTime']
    )


def reserve_seat(url: str, headers: dict, all_grab_seat: list):
    """
    预约座位

    :param url: 请求地址接口
    :param headers: 请求头
    :param all_grab_seat: 座位信息
    """
    flag = False
    for seat in all_grab_seat:
        while True:
            seat.pop("roomName", None)
            # 发送请求
            response = utils.request_util.send_get_request(url, seat, headers, timeout=30)
            data = json.loads(response.content)
            if data.get('ReturnValue', None) == -1 and data.get('Msg', '').startswith('当'):
                # 睡眠一秒
                time.sleep(1)
                continue
            elif data.get('ReturnValue', None) == -1 and data.get('Msg', '').startswith('此'):
                break
            elif data.get('ReturnValue', None) == -1 and data.get('Msg', '').startswith('预'):
                flag = True
                break
            elif data.get('ReturnValue', None) == -1 and data.get('Msg', '').startswith('您'):
                flag = True
                break
        if flag:
            msg = f"响应消息: {data.get('Msg', None)}, 返回值: {data.get('ReturnValue', None)}"
            # 记录日志
            add_logs(action_logger, "INFO", "预约座位", msg)
            break
    if not flag:
        # 记录日志
        add_logs(action_logger, "ERROR", "预约座位", exc_info=False)
        return print(f"\n[提示信息: 预约座位失败!]")
    # 获取预约历史记录
    reserve_history = get_reserve_history()
    # 获取当前预约记录
    reserve_record = get_reserve_record()
    print(f"\n[您预约的座位信息]")
    show_reserve_record_table(reserve_record)
    # 将当前预约记录插入到预约历史记录的开头, 并保留20条记录
    reserve_history = (reserve_record + reserve_history)[:20]
    # 写入配置文件
    utils.file_util.write_json(constant.RESERVE_HISTORY_FILE_PATH, reserve_history)
    encrypt_seats_property()


def clock_in():
    """签到"""
    user = get_user()
    reserve_record = get_reserve_record()
    # 判断是否有预约记录
    if not reserve_record:
        return print(f"\n[提示信息: 您没有需要签到的座位!]")
    # 提取公共信息
    url = get_server_url(user) + constant.CLOCK_IN_URL
    params = {
        "readerno": user.get('userInfo', {}).get('cardNo'),
        "password": user.get('userInfo', {}).get('passWord'),
        "tableno": "",
        "checkstr": "",
    }
    headers = {"Cookie": user.get('userInfo', {}).get('cookie')}
    for record in reserve_record:
        # 设置参数
        params['tableno'] = record.get('tableNo', '')
        params['checkstr'] = uuid.uuid4().hex
        # 发送签到请求
        response = utils.request_util.send_get_request(url, params, headers, timeout=30)
        data = json.loads(response.content)
        msg = f"响应消息: {data.get('Msg', None)}, 返回值: {data.get('ReturnValue', None)}"
        if data.get('ReturnValue', None) == 0:
            # 记录日志
            add_logs(action_logger, "INFO", "签到", msg)
            print(f"\n[提示信息: {data.get('Msg', None)}!]")
        else:
            # 记录日志
            add_logs(action_logger, "ERROR", "签到", msg, exc_info=False)
            print(f"\n[提示信息: {data.get('Msg', None)}!]")


# 全局退出标志
exit_event = threading.Event()


def print_current_time():
    """显示当前时间"""
    while not exit_event.is_set():
        print(f"\r当前时间是[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]", end="")
        time.sleep(1)


def run_scheduler():
    """运行定时任务"""
    while not exit_event.is_set():
        schedule.run_pending()
        time.sleep(1)


def start_grab_seat():
    """开始进行抢座"""
    grab_seat = get_grab_seat()
    print_grab_seat(grab_seat, 'forenoon')
    print_grab_seat(grab_seat, 'afternoon')
    common.info_tip("如果信息不正确, 请重新配置抢座信息")
    flag = input("是否开始本次抢座?(y/n)")
    if flag == 'n' or flag == 'N':
        common.info_tip("你已取消抢座")
        return False
    # 提取公共信息
    user = get_user()
    url = get_server_url(user) + constant.BESK_SEAT_URL
    headers = {"Cookie": user['userInfo']['cookie']}
    grab_seat_task_exists = False
    clock_in_task_exists = False
    # 添加抢座和签到任务到全局调度器
    for time_slot in timeSlotSet:
        tip = "上午" if time_slot == "forenoon" else "下午"
        slot_data = grab_seat.get(time_slot, {})
        all_grab_seat = []
        start_time = slot_data.get('startTime', None)
        clock_in_time = slot_data.get('clockInTime', None)
        for seat_type in ['fixedSeat', 'autoSeat']:
            all_grab_seat += slot_data.get(seat_type, [])
        # 添加抢座任务到全局调度器
        if all_grab_seat and start_time:
            grab_seat_task_exists = True
            # 添加定时任务
            schedule.every().day.at(start_time).do(reserve_seat, url=url, headers=headers, all_grab_seat=all_grab_seat)
            # 记录日志
            add_logs(action_logger, "INFO", f"添加{tip}抢座任务")
            common.info_tip(f"添加{tip}抢座任务成功")
        # 添加签到任务到全局调度器
        if clock_in_time:
            clock_in_task_exists = True
            # 添加定时任务
            schedule.every().day.at(clock_in_time).do(clock_in)
            # 记录日志
            add_logs(action_logger, "INFO", f"添加{tip}签到任务")
            common.info_tip(f"添加{tip}签到任务成功")
    if not grab_seat_task_exists:
        return common.info_tip("暂时没有预约任务")
    if not clock_in_task_exists:
        # 记录日志
        add_logs(action_logger, "ERROR", f"添加签到任务", err_msg="暂无签到任务", exc_info=False)
    print("开始抢座, 请稍等...")
    # 启动定时任务线程和显示时间线程
    threading.Thread(target=run_scheduler, daemon=True).start()
    threading.Thread(target=print_current_time, daemon=True).start()
    try:
        # 保持主线程活跃
        while True:
            # 避免高频占用 CPU
            time.sleep(3600)
    except KeyboardInterrupt:
        # 设置退出标志
        exit_event.set()
        print(f"\n[提示信息: 用户手动退出抢座!]")
        # 等待子线程结束输出
        time.sleep(1)


def show_reserve_history():
    """显示预约历史记录"""
    reserve_history = get_reserve_history()
    if not reserve_history:
        return common.info_tip("暂无预约记录")
    show_reserve_record_table(reserve_history)
